﻿using MailModel;
using System;
using System.Collections;
using System.Data;
using System.Data.Entity;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;

namespace MailDAL
{
    public class GenericRepository<TEntity> where TEntity : class
    {
        internal MailToolEntities Entities { get; private set; }
        internal DbSet<TEntity> DBSet { get; private set; }

        public GenericRepository(MailToolEntities entities)
        {
            if (entities == null)
                throw new ArgumentNullException();

            Entities = entities;
            DBSet = Entities.Set<TEntity>();
        }

        public virtual TEntity Create()
        {
            return DBSet.Create();
        }

        public virtual TEntity Attach(object obj)
        {
            TEntity entity = Create();

            Type typeObj = obj.GetType();
            Type typeEntity = entity.GetType();

            PropertyInfo[] arrayPiObj = typeObj.GetProperties();
            PropertyInfo[] arrayPiEntity = typeEntity.GetProperties();

            foreach (PropertyInfo pi in arrayPiObj)
            {
                var result = (from piEntity in arrayPiEntity
                              where piEntity.Name == pi.Name
                              select piEntity).SingleOrDefault();
                if (result != null)
                    result.SetValue(entity, pi.GetValue(obj, null), null);
            }

            return DBSet.Attach(entity);
        }

        public virtual IQueryable<TEntity> Get(
            Expression<Func<TEntity, bool>> filter = null,
            Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
            int count = 0,
            int skip = 0,
            params Expression<Func<TEntity, object>>[] includeProperties)
        {
            IQueryable<TEntity> query = DBSet;

            if (filter != null)
            {
                query = query.Where(filter);
            }

            foreach (var x in includeProperties)
            {
                query = query.Include(x);
            }

            if (orderBy != null)
            {
                query = orderBy(query);
            }

            if (skip > 0)
            {
                query = query.Skip(skip);
            }

            if (count > 0)
            {
                query = query.Take(count);
            }

            return query;
        }

        public virtual IEnumerable GetValues(string fieldName, Expression<Func<TEntity, bool>> filter = null)
        {
            throw new NotImplementedException();
        }

        public virtual TEntity GetByKey(params object[] keyValues)
        {
            return DBSet.Find(keyValues);
        }

        public virtual void Insert(TEntity entity)
        {
            DBSet.Add(entity);
        }

        #region Delete() overload +3
        public virtual void Delete(object id)
        {
            TEntity entityToDelete = DBSet.Find(id);
            Delete(entityToDelete);
        }

        public virtual void Delete(TEntity entityToDelete)
        {
            if (Entities.Entry(entityToDelete).State == EntityState.Detached)
            {
                DBSet.Attach(entityToDelete);
            }
            DBSet.Remove(entityToDelete);
        }

        public virtual void Delete(Expression<Func<TEntity, bool>> filter)
        {
            IQueryable<TEntity> query = DBSet;
            if (filter != null)
                query = query.Where(filter);

            foreach (TEntity entity in query)
                Delete(entity);
        }
        #endregion

        public virtual void Update(TEntity entityToUpdate)
        {
            DBSet.Attach(entityToUpdate);
            Entities.Entry(entityToUpdate).State = EntityState.Modified;
        }
    }
}
